package sk.naive.talker.server;

import sk.naive.talker.adapter.*;
import sk.naive.talker.*;
import sk.naive.talker.plugin.TalkerPlugin;
import sk.naive.talker.command.Motd;
import sk.naive.talker.message.*;
import sk.naive.talker.persistence.*;
import sk.naive.talker.util.*;
import sk.naive.talker.props.*;

import java.rmi.RemoteException;
import java.security.NoSuchAlgorithmException;

/**
 * UserLoginHelper.
 *
 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
 * @version $Revision: 1.9 $ $Date: 2005/02/16 19:27:21 $
 */
public class UserLoginHelper {
	private Talker talker;
	private AdapterUser adapterUser;

	public UserLoginHelper(Talker talker, AdapterUser adapterUser) {
		this.talker = talker;
		this.adapterUser = adapterUser;
	}

	public void process() {
	}

	/**
	 * Authenticate user and returns his id.
	 *
	 * @return id of autheticated user
	 * @throws RemoteException
	 * @throws sk.naive.talker.AuthenticationException
	 * @throws sk.naive.talker.persistence.PersistenceException
	 */
	public Integer authenticate()
		throws RemoteException,
		AuthenticationException,
		PersistenceException
	{
		try {
			Integer userId = null;
			String authMethod = (String) adapterUser.get(User.UPROP_AUTH_METHOD);
			if (authMethod == null) {
				authMethod = "";
			}
			String login = (String) adapterUser.get(User.UPROP_LOGIN);
			String password = Utils.passwordString((String) adapterUser.get(User.UPROP_PASSWORD));
			if (authMethod.equals(User.AUTH_METHOD_NEW)) {
				Integer existingId = new DbLayer().userId(login);
				if (existingId == null) {
					testLoginName(login);
					userId = createNewUser(login, password);
				}
				if (existingId != null) {
					throw new AuthenticationException("User authentized with method new already exists (" + login + ").");
				}
				return userId;
			}
			// default authentication
			DbLayer dbLayer = new DbLayer();
			Integer id = dbLayer.userId(login);
			String realPassword = dbLayer.loadProperty(id, User.UPROP_PASSWORD);
			if (realPassword != null && realPassword.equals(password)) {
				userId = id;
			} else {
				throw new AuthenticationException("Invalid login/password.");
			}
			return userId;
		} catch (NoSuchAlgorithmException e) {
			throw new AuthenticationException("No such algorithm.", e);
		}
	}

	private void testLoginName(String login) throws AuthenticationException {
		if (login.length() < 2) {
			throw new AuthenticationException("Login is too short - at least 2 characters are required.");
		}
		if (login.length() > 16) {
			throw new AuthenticationException("Login is too long - at most 16 characters are allowed.");
		}
	}

	private Integer createNewUser(String login, String password) throws PersistenceException {
		Integer userId = null;
		DbTransaction.begin();
		try {
			DbLayer db = new DbLayer();
			userId = db.createNewUser();
			if (userId == 2) {
				db.storeProperty(userId, PermissionProperty.UPROP_PERMISSION, Consts.PERM_SUPERUSER);
				talker.getLogger().info("Superuser permissions granted to created user with id=" + userId + ".");
			}
			db.storeProperty(userId, User.UPROP_NAME, (String) adapterUser.get(User.UPROP_NAME));
			db.storeProperty(userId, User.UPROP_LOGIN, login);
			db.storeProperty(userId, SexProperty.UPROP_SEX, (String) adapterUser.get(SexProperty.UPROP_SEX));
			db.storeProperty(userId, User.UPROP_PASSWORD, password);
			db.storeProperty(userId, User.UPROP_LANG, (String) adapterUser.get(User.UPROP_LANG));
			db.storeProperty(userId, User.UPROP_DEFAULT_COMMAND, "say");
			db.storeProperty(userId, User.UPROP_COMMAND_SIGN, ".");
			db.storeProperty(userId, User.UPROP_CREATED, String.valueOf(System.currentTimeMillis()));
		} catch (RemoteException e) {
			Utils.unexpectedExceptionWarning(e);
			DbTransaction.setRollbackOnly(e.getMessage());
		} catch (PersistenceException e) {
			Utils.unexpectedExceptionWarning(e);
			DbTransaction.setRollbackOnly(e.getMessage());
		}
		DbTransaction.finish();
		return userId;
	}

	public void welcomeUser(final UserProxy up, boolean relogin) throws Exception {
		UserExcluder ue = new UserIgnoreExcluder(up, Category.SYSTEM);
		Location loc;
		if (relogin) {
			talker.getLogger().fine("Relogin: " + up.getLogin());
			talker.sendToUsers(talker.messageFactory().createMessage("server.relogin", up.getProperties()), talker.userFinder().onlineUsers(), ue);
			up.send(talker.messageFactory().getString("server.reloginWelcome", up.getProperties()));
		} else {
			talker.getLogger().fine("In: " + up.getLogin());
			up.send(talker.messageFactory().getString("server.loginWelcome", up.getProperties()));
			UserContextHelper userContextHelper = new UserContextHelper(up);
			// TODO: konstanta v prikaze? :-) niekam inam supnut
			up.send(talker.get(Motd.TPROP_MOTD) + Utils.tag(TagConsts.BR));
			if (BooleanProperty.booleanValue((String) up.get(User.UPROP_SAVE_LAST_LOCATION))) {
				loc = talker.locationFinder().findLocationByName((String) up.get(User.UPROP_LAST_LOCATION));
				userContextHelper.set(DefaultMessageFactory.CTXKEY_VAL, loc.getName());
				talker.sendToUsers(talker.messageFactory().createMessage("server.loginWithRoom", up.getProperties()), talker.userFinder().onlineUsers(), ue);
			} else {
				loc = talker.locationFinder().findLocationByName((String) talker.get("defaultRoom"));
				talker.sendToUsers(talker.messageFactory().createMessage("server.login", up.getProperties()), talker.userFinder().onlineUsers(), ue);
			}
			userContextHelper.cleanup();
			if (loc != null) {
				loc.userIn(up);
			}
			for (TalkerPlugin plugin : talker.getPlugins()) {
				try {
					plugin.userIn(up);
				} catch (Throwable t) {
					Utils.unexpectedExceptionWarning(t);
				}
			}
		}
		talker.getCommandDispatcher().process("look", up, null);
	}
}
